import os;
import re;
import sys;
import ssl;
import dbx;
import stdx;
import gzip;
import html;
import socket;
import urllib;
import logging;
import datetime;
from ctypes import *;
from io import BytesIO;
from html.parser import HTMLParser;

try:
	import httplib;
except:
	import http.client as httplib;

httpsvr = None;
httpath = None;
httport = None;
httphost = None;
dbpoolmap = None;

ssl._create_default_https_context = ssl._create_unverified_context;
ssl.match_hostname = lambda cert, hostname: hostname == cert['subjectAltName'][0][1];

def pystr(str):
	if str == None: return None;
	return str.decode();

def cppstr(str):
	if str == None: return None;
	return str.encode();

def encode(str):
	return urllib.parse.quote(str);

def decode(str):
	str = str.replace('+', ' ');
	return urllib.parse.unquote(str);

def escape(str):
	return html.escape(str);

def unescape(str):
	return html.unescape(str);

def gzipdecode(msg):
	return gzip.decompress(msg);

def gzipencode(msg, level = 9):
	with BytesIO() as fd:
		with gzip.GzipFile(mode = 'wb', compresslevel = level, fileobj = fd) as zfile:
			zfile.write(msg);
		fd.seek(0);
		return fd.read();
	return None;

def InitDatabase():
	global httpsvr;
	global dbpoolmap;
	pool = None;
	name = GetConfig('database.name');
	if name.find('$') >= 0 or name.find('%') >= 0 or name.find('`') >= 0:
		with os.popen('echo %s' % name) as fd: name = fd.read().strip();
	if name.find('/') >= 0 or name.find('\\') >= 0:
		import pysqlite3;
		pool = dbx.DBConnectPool(pysqlite3, name);
	else:
		import pymysql;
		pool = dbx.DBConnectPool(pymysql, name, GetConfig('database.host'), GetConfig('database.port'), GetConfig('database.user'), GetConfig('database.password'));
	dbx.SetConnectPool(pool);
	with dbx.GetConnect() as db:
		dbpoolmap = {};
		for item in db.queryArray('SELECT ID,TYPE,NAME,HOST,PORT,USER,PASSWORD,CHARSET FROM T_XG_DBETC WHERE ENABLED>0'):
			type = item['type'].lower();
			dbid = item['id'];
			pool = None;
			try:
				name = item['name'];
				charset = item['charset'].lower().replace('-', '');
				if name.find('$') >= 0 or name.find('%') >= 0 or name.find('`') >= 0:
					with os.popen('echo %s' % name) as fd: name = fd.read().strip();
				if type.find('mysql') >= 0:
					import pymysql;
					pool = dbx.DBConnectPool(pymysql, name, item['host'], int(item['port']), item['user'], item['password'], charset);
				elif type.find('sqlite') >= 0:
					import pysqlite3;
					pool = dbx.DBConnectPool(pysqlite3, name);
				else:
					LogTrace('ERR', 'unsupported database[%s]' % dbid);
			except BaseException as e:
				LogTrace('ERR', 'connect database[%s] failed[%s]' % (dbid, str(e)), e);
			if pool:
				LogTrace('IMP', 'connect database[%s] success' % dbid);
				dbpoolmap[dbid] = pool;

def InitWebapp(path):
	global httpsvr;
	global httpath;
	global httport;
	global httphost;

	httpsvr = cdll.LoadLibrary(os.environ['CPPWEB_INSTALL_HOME'] + '/webapp/etc/plugin/bin/InitSystem.so');
	httpsvr.SetLogFlag.argtypes = [c_int];
	httpsvr.WriteLog.argtypes = [c_char_p, c_char_p];
	httpsvr.GetId.restype = c_int;
	httpsvr.GetPort.restype = c_int;
	httpsvr.GetHost.restype = c_char_p;
	httpsvr.GetSequence.restype = c_char_p;
	httpsvr.GetConfig.restype = c_char_p;
	httpsvr.GetConfig.argtypes = [c_char_p, c_char_p];
	httpsvr.GetMimeType.restype = c_char_p;
	httpsvr.GetMimeType.argtypes = [c_char_p];
	httpsvr.GetParameter.restype = c_char_p;
	httpsvr.GetParameter.argtypes = [c_char_p];
	httpsvr.GetRouteHost.restype = c_char_p;
	httpsvr.GetRouteHost.argtypes = [c_char_p];
	httpsvr.SetCgiAccess.argtypes = [c_char_p, c_char_p];
	httpsvr.SetCgiExtdata.argtypes = [c_char_p, c_char_p];
	httpsvr.DisableSession.restype = c_int;
	httpsvr.DisableSession.argtypes = [c_char_p];
	httpsvr.CreateSession.restype = c_int;
	httpsvr.CreateSession.argtypes = [c_char_p, c_int];
	httpsvr.GetSession.restype = c_char_p;
	httpsvr.GetSession.argtypes = [c_char_p, c_char_p];
	httpsvr.SetSession.restype = c_int;
	httpsvr.SetSession.argtypes = [c_char_p, c_char_p, c_char_p];
	httpsvr.GetLastRemoteStatus.restype = c_int;
	httpsvr.GetRemoteResult.restype = c_char_p;
	httpsvr.GetRemoteResult.argtypes = [c_char_p, c_char_p, c_char_p, c_char_p];
	httpsvr.CheckAccess.restype = c_int;
	httpsvr.CheckAccess.argtypes = [c_char_p, c_char_p, c_char_p];

	httpath = path;
	httport = httpsvr.GetPort();
	httphost = pystr(httpsvr.GetHost());

	return httpsvr;

def GetPort():
	return httport;

def GetHost():
	return httphost;

def GetPath():
	return httpath;

def GetSequence():
	return pystr(httpsvr.GetSequence());

def GetParameter(key):
	return pystr(httpsvr.GetParameter(cppstr(key)));

def GetMimeType(key):
	return pystr(httpsvr.GetMimeType(cppstr(key)));

def GetConfig(name, key = None):
	return pystr(httpsvr.GetConfig(cppstr(name), cppstr(key)));

def SetCgiAccess(path, access):
	httpsvr.SetCgiAccess(cppstr(path), cppstr(access));

def SetCgiExtdata(path, extdata):
	httpsvr.SetCgiExtdata(cppstr(path), cppstr(extdata));

def CheckAccess(path, param, grouplist):
	return httpsvr.CheckAccess(cppstr(path), cppstr(param), cppstr(grouplist));

def GetLastRemoteStatus():
	return httpsvr.GetLastRemoteStatus();

def GetRemoteResult(path, param = None, contype = None, cookie = None):
	return pystr(httpsvr.GetRemoteResult(cppstr(path), cppstr(param), cppstr(contype), cppstr(cookie)));

def DisableSession(sid):
	if sid: httpsvr.DisableSession(cppstr(sid));

def SetSession(sid, key, val):
	return httpsvr.SetSession(cppstr(sid), cppstr(key), cppstr(val));

def GetSession(sid, key = None):
	return pystr(httpsvr.GetSession(cppstr(sid), cppstr(key)));

def CreateSession(sid, timeout = 0):
	return httpsvr.CreateSession(cppstr(sid), timeout);

def GetDBConnect(dbid = None):
	conn = None;
	global dbpoolmap;
	if dbid == None or len(dbid) <= 0: conn = dbx.GetConnect();
	if dbid in dbpoolmap: conn = dbpoolmap[dbid].get();
	if conn: conn._log = LogTrace;
	return conn;

def LogTrace(tag, msg, err = None):
	if err: logging.exception(err);
	if httpsvr: httpsvr.WriteLog(tag.encode(), msg.encode());
	else: stdx.puts(msg);

class HttpRequest:
	def __init__(self, head = None, data = None):
		if head == None and len(sys.argv) > 1: head = decode(sys.argv[1]);
		if data == None and len(sys.argv) > 2: data = sys.argv[2];
		self.parse(head, data);
	def clear(self):
		self._head = {};
		self._data = {};
		return self;
	def toString(self):
		head = '';
		data = '';
		for key in self._head:
			head = head + key + ':' + self._head[key] + '\r\n';
		for key in self._data:
			data = data + key + '=' + encode(self._data[key]) + '&';
		if len(head) > 2: head = encode(head[0:-2]);
		if len(data) > 1: data = data[0:-1];
		return (head, data);
	def parse(self, head, data):
		if head == None or len(head) <= 0:
			self._head = {};
		else:
			head = head.split('\r\n');
			tmp = {};
			for str in head:
				arr = str.split(':');
				if len(arr) == 1: arr.append('');
				tmp[arr[0].strip()] = arr[1].strip(' \r\n\t+');
			self._head = tmp;
		if data == None or len(data) <= 0:
			self._data = {};
		else:
			data = data.split('&');
			tmp = {};
			for str in data:
				arr = str.split('=');
				if len(arr) == 1: arr.append('');
				tmp[arr[0].strip()] = decode(arr[1]);
			self._data = tmp;
	def getParameters(self):
		return self._data;
	def getHeadParameters(self):
		return self._head;
	def getParameter(self, key):
		if key in self._data: return self._data[key];
		else: return '';
	def getHeadParameter(self, key):
		if key in self._head: return self._head[key];
		else: return '';
	def setParameter(self, key, val):
		if not isinstance(key, str): key = str(key);
		if not isinstance(val, str): val = str(val);
		if self._data == None: self._data = {key: val};
		else: self._data[key] = val;
	def setHeadParameter(self, key, val):
		if not isinstance(key, str): key = str(key);
		if not isinstance(val, str): val = str(val);
		if self._head == None: self._head = {key: val};
		else: self._head[key] = val;

class XMLParser(HTMLParser):
	def __init__(self, msg = None):
		HTMLParser.__init__(self);
		self._idx = 0;
		self._tag = '';
		self._attr = [];
		self._path = [];
		self._attrlist = [];
		if msg: self.feed(msg);
	def __len__(self):
		return len(self._attr);
	def __iter__(self):
		self._idx = 0;
		return self;
	def __next__(self):
		if self._idx < len(self._attr):
			data = self._attr[self._idx];
			self._idx += 1;
			return data;
		else:
			raise StopIteration();
	def __getitem__(self, key):
		if isinstance(key, str):
			for item in self._attr:
				if key == item[0]: return item[1];
		elif isinstance(key, int):
			if key < len(self._attr):
				return self._attr[key][1];
		return None;
	def attr(self):
		return self._attr;
	def path(self, idx = None):
		if isinstance(idx, int): return self._path[idx];
		else: return '/' + '/'.join(self._path);
	def handle_data(self, text):
		return self.process(self._tag, text);
	def process(self, tag, text):
		pass;
	def handle_endtag(self, tag):
		while len(self._path) > 0:
			if tag == self._path.pop():
				self._attrlist.pop();
				if len(self._attrlist) > 0: self._attr = self._attrlist[-1];
				else: self._attr = [];
				if len(self._path) > 0: self._tag = self._path[-1];
				else: self._tag = '';
				break;
	def handle_starttag(self, tag, attr):
		self._tag = tag;
		self._attr = attr;
		self._path.append(tag);
		self._attrlist.append(attr);
		return self.process(tag, None);

def GetInfoFromURL(url):
	port = 80;
	param = '';
	crypted = False;
	if url[:7] == 'http://' or url[:7] == 'HTTP://':
		crypted = False;
		url = url[7:];
	elif url[:8] == 'https://' or url[:8] == 'HTTPS://':
		crypted = True;
		url = url[8:];
		port = 443;
	m = url.find(':');
	n = url.find('?');
	k = url.find('/');
	if m > 0: end = url[m + 1:];
	if n > 0: ptr = url[n + 1:];
	if m > 0 and end[0] > '0' and end[0] <= '9' and (n < 0 or n > m):
		port = stdx.atoi(url[m + 1:]);
		host = url[:m];
	else:
		if m > 0: host = url[:m];
		elif k > 0: host = url[:k];
		elif n > 0: host = url[:n];
		else: host = url;
	if k > 0: param = url[k:];
	elif n > 0: param = '/' + url[n:];
	if len(param) == 0: param = '/';
	ip = socket.gethostbyname(host);
	if crypted:
		if port == 443: site = 'https://' + host;
		else: site = 'https://' + host + ':' + str(port);
	else:
		if port == 80: site = 'http://' + host;
		else: site = 'http://' + host + ':' + str(port);
	return stdx.fromdict({'ip': ip, 'port': port, 'host': host, 'site': site, 'param': param, 'crypted': crypted});

def GetLinkSet(str, host = None):
	us = set();
	strsz = len(str);
	taglist = ['http://', 'https://', 'href=', 'src=', 'url('];
	for tag in taglist:
		cnt = len(tag);
		pos = str.find(tag, 0);
		while pos > 0 and pos < strsz:
			pos = pos + cnt;
			n = pos + 256;
			a = str.find('\'', pos, n);
			if a < 0: a = 0xFFFFFFF;
			b = str.find('\"', pos, n);
			if b < 0: b = 0xFFFFFFF;
			c = str.find('>', pos, n);
			if c < 0: c = 0xFFFFFFF;
			d = str.find('<', pos, n);
			if d < 0: d = 0xFFFFFFF;
			e = str.find(')', pos, n);
			if e < 0: e = 0xFFFFFFF;
			f = str.find(';', pos, n);
			if f < 0: f = 0xFFFFFFF;
			g = str.find(' ', pos, n);
			if g < 0: g = 0xFFFFFFF;
			m = str.find('\r', pos, n);
			if m < 0: m = 0xFFFFFFF;
			n = str.find('\n', pos, n);
			if n < 0: n = 0xFFFFFFF;
			end = min(a, b, c, d, e, f, g, m, n);
			if end > 0:
				url = str[pos:end];
				pos = str.find(tag, end + 1);
				url = url.strip(' \r\n\t(;)\'\"');
				if len(url) > 0:
					if host and not url.startswith('http'):
						if url[0] == '/': url = host + url;
						else: url = host + '/' + url;
					us.add(url);
			else:
				pos = str.find(tag, pos + 1);
	return us;

def GetHttpResult(url, param = None, head = {}, timeout = 5):
	for i in (1,2,3):
		data = GetInfoFromURL(url);
		head['Host'] = data.host;
		tmp = url.lower();
		if tmp.find('http:') < 0 and tmp.find('https:') < 0:
			url = 'http://' + url;
		if data.crypted:
			conn = httplib.HTTPSConnection(data.ip, data.port, timeout = timeout);
		else:
			conn = httplib.HTTPConnection(data.ip, data.port, timeout = timeout);
		if param:
			conn.request('POST', data.param, urllib.parse.urlencode(param), head);
		else:
			conn.request('GET', data.param, headers = head);
		res = conn.getresponse();
		if (res.getcode() == 200):
			setattr(res, 'host', data);
			return res;
		url = res.getheader('Location');
		if len(url) <= 0:
			url = res.getheader('location');
			if len(url) <= 0: return res;
	return None;
